<template>
    <div class="flex">
        <div class="echart-box">
            <V3Echarts :width="210" :height="props.height" :options="option" :top="10" isFirst />
        </div>

        <div class="legend">
            <div v-for="item in optionsData" :key="item.name">
                <div class="item" v-if="item.name">
                    <div class="item-left">
                        <div class="circle" :style="'background:' + item.itemStyle.color"></div>
                        <div class="label">{{ item.name }}</div>
                    </div>
                    <div class="item-right">
                        <div class="count">{{ item.value }}{{ props.unit }}</div>
                        <div class="ratio" v-if="ratio">{{ ((item.value / sum) * 100).toFixed(0) }}%</div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script lang="ts" setup>
import { ref, watch } from 'vue'
import V3Echarts from '@/stories/components/V3Echarts/index.vue'
import 'echarts-gl'

const props = defineProps({
    chartData: {
        type: Array,
        default: () => {
            return [
                {
                    name: '十分满意',
                    value: 824
                },
                {
                    name: '满意',
                    value: 62
                },
                {
                    name: '一般',
                    value: 11
                },
                {
                    name: '差评',
                    value: 1
                }
            ]
        }
    },
    color: {
        type: Array,
        default: () => ['#16c3be', '#8caae3', '#f3f8ff', '#d7825d', '#e8e5a0', '#61a5eb']
    },
    unit: {
        type: String,
        default: ''
    },
    height: {
        type: Number,
        default: 150
    },
    ratio: {
        type: Boolean,
        default: true
    }
})

let optionsData: any = []
let sum = 0
let option: any = ref(null)

const initChart = () => {
    sum = 0
    optionsData = []
    props.chartData.forEach((item: any, index: number) => {
        sum += item.value
        const newItem = JSON.parse(JSON.stringify(item))
        newItem.itemStyle = {
            color: props.color[index]
        }
        optionsData.push(newItem)
    })

    const series = getPie3D(optionsData, null)

    option.value = {
        color: props.color,
        tooltip: {
            confine: true,
            formatter: (params: any) => {
                if (params.seriesName !== 'mouseoutSeries' && params.seriesName !== 'pie2d') {
                    let bfb = ((option.value.series[params.seriesIndex].pieData.endRatio - option.value.series[params.seriesIndex].pieData.startRatio) * 100).toFixed(2)
                    return `${params.seriesName}<br/>` + `<span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${params.color};"></span>` + `${bfb}%`
                }
            }
        },
        labelLine: {
            show: true,
            lineStyle: {
                color: '#7BC0CB'
            }
        },
        label: {
            show: true,
            position: 'outside',
            formatter: '{b} \n{c} {d}%'
        },
        xAxis3D: {
            min: -1,
            max: 1
        },
        yAxis3D: {
            min: -1,
            max: 1
        },
        zAxis3D: {
            min: -1,
            max: 1
        },
        grid3D: {
            show: false,
            boxHeight: 20, // 三维笛卡尔坐标系在三维场景中的高度
            viewControl: {
                alpha: 35,
                beta: 40,
                distance: 350, //调整视角到主体的距离，类似调整zoom
                rotateSensitivity: 0, // 设置为0无法旋转
                zoomSensitivity: 0, // 设置为0无法缩放
                panSensitivity: 0, // 设置为0无法平移
                autoRotate: false // 自动旋转
            },
            light: {
                main: {
                    color: '#dddddd', // 主光源的颜色。
                    shadow: true, // 主光源是否投射阴影
                    alpha: 80 // 主光源绕 x 轴，即上下旋转的角度
                }
            }
        },
        series: series
    }
}

watch(
    () => props,
    newValue => {
        initChart()
    },
    { immediate: true, deep: true }
)

// 生成扇形的曲面参数方程，用于 series-surface.parametricEquation
function getParametricEquation(startRatio: number, endRatio: number, isSelected: boolean, isHovered: boolean, k: any, h: any) {
    // 计算
    const midRatio = (startRatio + endRatio) / 2
    const startRadian = startRatio * Math.PI * 2
    const endRadian = endRatio * Math.PI * 2
    const midRadian = midRatio * Math.PI * 2
    // 如果只有一个扇形，则不实现选中效果。
    if (startRatio === 0 && endRatio === 1) {
        isSelected = false
    }
    // 通过扇形内径/外径的值，换算出辅助参数 k（默认值 1/3）
    k = 1
    // 计算选中效果分别在 x 轴、y 轴方向上的位移（未选中，则位移均为 0）
    const offsetX = isSelected ? Math.cos(midRadian) * 0.1 : 0
    const offsetY = isSelected ? Math.sin(midRadian) * 0.1 : 0
    // 计算高亮效果的放大比例（未高亮，则比例为 1）
    const hoverRate = isHovered ? 1.05 : 1
    // 返回曲面参数方程
    return {
        u: {
            min: -Math.PI,
            max: Math.PI * 3,
            step: Math.PI / 32
        },
        v: {
            min: 0,
            max: Math.PI * 2,
            step: Math.PI / 20
        },
        x: function (u: any, v: any) {
            if (u < startRadian) {
                return offsetX + Math.cos(startRadian) * (1 + Math.cos(v) * k) * hoverRate
            }
            if (u > endRadian) {
                return offsetX + Math.cos(endRadian) * (1 + Math.cos(v) * k) * hoverRate
            }
            return offsetX + Math.cos(u) * (1 + Math.cos(v) * k) * hoverRate
        },
        y: function (u: any, v: any) {
            if (u < startRadian) {
                return offsetY + Math.sin(startRadian) * (1 + Math.cos(v) * k) * hoverRate
            }
            if (u > endRadian) {
                return offsetY + Math.sin(endRadian) * (1 + Math.cos(v) * k) * hoverRate
            }
            return offsetY + Math.sin(u) * (1 + Math.cos(v) * k) * hoverRate
        },
        z: function (u: any, v: any) {
            if (u < -Math.PI * 0.5) {
                return Math.sin(u)
            }
            if (u > Math.PI * 2.5) {
                return Math.sin(u) * h * 0.1
            }
            return Math.sin(v) > 0 ? 1 * h * 0.1 : -1
        }
    }
}

/**
 * 绘制3d图
 * @param pieData 总数据
 * @param internalDiameterRatio:透明的空心占比
 */
function getPie3D(pieData: any, internalDiameterRatio: any) {
    const series = []
    let sumValue = 0
    let startValue = 0
    let endValue = 0
    const legendData = []
    const k = typeof internalDiameterRatio !== 'undefined' ? (1 - internalDiameterRatio) / (1 + internalDiameterRatio) : 1 / 3
    // 为每一个饼图数据，生成一个 series-surface 配置
    for (let i = 0; i < pieData.length; i += 1) {
        sumValue += pieData[i].value
        const seriesItem: any = {
            name: typeof pieData[i].name === 'undefined' ? `series${i}` : pieData[i].name,
            type: 'surface',
            parametric: true,
            wireframe: {
                show: false
            },
            pieData: pieData[i],
            pieStatus: {
                selected: false,
                hovered: false,
                k: k
            }
        }
        if (typeof pieData[i].itemStyle !== 'undefined') {
            const itemStyle: any = {}
            if (typeof pieData[i].itemStyle.color !== 'undefined') {
                itemStyle.color = pieData[i].itemStyle.color
            }
            if (typeof pieData[i].itemStyle.opacity !== 'undefined') {
                itemStyle.opacity = pieData[i].itemStyle.opacity
            }
            seriesItem.itemStyle = itemStyle
        }
        series.push(seriesItem)
    }
    // 使用上一次遍历时，计算出的数据和 sumValue，调用 getParametricEquation 函数，
    // 向每个 series-surface 传入不同的参数方程 series-surface.parametricEquation，也就是实现每一个扇形。
    for (let i = 0; i < series.length; i += 1) {
        endValue = startValue + series[i].pieData.value
        series[i].pieData.startRatio = startValue / sumValue
        series[i].pieData.endRatio = endValue / sumValue
        series[i].parametricEquation = getParametricEquation(series[i].pieData.startRatio, series[i].pieData.endRatio, false, false, k, 20)
        startValue = endValue
        legendData.push(series[i].name)
    }
    return series
}
</script>

<style lang="scss" scoped>
.flex {
    width: 100%;
    display: flex;
    align-items: center;
}

.echart-box {
    background-size: 80% 80%;
    background: url('../img/饼图_bg@2x.png') no-repeat 50% 50%;
}
.legend {
    width: 260px;
    height: v-bind('props.height');
    padding-top: 10px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    .item {
        margin: 2px 5px;
        padding: 5px;
        display: flex;
        align-items: center;
        justify-content: space-between;
        .item-left {
            display: flex;
            align-items: center;
            .circle {
                width: 10px;
                height: 10px;
                border-radius: 50%;
                margin: 0 5px;
            }
            .label {
                font-size: 14px;
                color: #ddd;
            }
        }
        .item-right {
            display: flex;
            align-items: center;
            .count {
                font-size: 18px;
                margin-right: 5px;
                font-family: Tencent;
                color: #86dbff;
                text-align: right;
            }

            .ratio {
                font-size: 16px;
                width: 38px;
                text-align: right;
                font-family: Tencent;
                color: #ffffff;
            }
        }
    }
}
</style>
